package com.start.security.data;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.start.common.exceptions.StartBusException;
import com.start.common.tools.SpringContextTools;
import com.start.common.tools.StartRedisTools;
import com.start.security.configs.StartDataSecurityProperties;
import com.start.security.data.encryption.sm2.Sm2Util;
import com.start.security.model.ClientRequestDataModel;
import com.start.security.service.StartSecurityService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 完了请求解密
 */
@Slf4j
@ControllerAdvice
@ConditionalOnProperty(prefix = StartDataSecurityProperties.PREFIX, name = "enable", havingValue = "true")
public class StartRequestBodyAdvice extends RequestBodyAdviceAdapter {
    private final StartDataSecurityProperties configuration;
    private final StartSecurityService startSecurityDataService;
    private final StartRedisTools startRedisTools;
    private final ObjectMapper objectMapper;

    public StartRequestBodyAdvice(StartSecurityService startSecurityDataService, StartDataSecurityProperties configuration, StartRedisTools startRedisTools, ObjectMapper objectMapper) {
        this.configuration = configuration;
        this.objectMapper = objectMapper;
        this.startRedisTools = startRedisTools;
        this.startSecurityDataService = startSecurityDataService;
    }

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        HttpServletRequest httpServletRequest = SpringContextTools.getHttpServletRequest();
        if (!startSecurityDataService.hasDataSecurity(httpServletRequest)) {
            return false;
        }
        return methodParameter.hasParameterAnnotation(RequestBody.class);
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        InputStream is = inputMessage.getBody();
        byte[] data = new byte[is.available()];
        is.read(data);
        String dataStr = new String(data, StandardCharsets.UTF_8);
        InputStream inputStream;
        if (StringUtils.isEmpty(dataStr)) {
            inputStream = new ByteArrayInputStream(data);
            return new StartHttpInputMessage(inputMessage.getHeaders(), inputStream);
        }
        ClientRequestDataModel model = objectMapper.readValue(dataStr, ClientRequestDataModel.class);
        if (StringUtils.isEmpty(model.getData())) {
            inputStream = new ByteArrayInputStream(data);
            return new StartHttpInputMessage(inputMessage.getHeaders(), inputStream);
        }
        HttpHeaders headers = inputMessage.getHeaders();
        List<String> strings = headers.get(configuration.getHeardClientKey());
        if (strings == null || strings.isEmpty()) {
            throw new StartBusException("已开启数据保护：请在header里面添加key:" + configuration.getHeardClientKey() + ",值为您的客户端id");
        }
        String clientId = strings.get(0);
        if (StringUtils.isEmpty(clientId)) {
            throw new StartBusException("已开启数据保护：请在header里面添加key:" + configuration.getHeardClientKey() + ",值为您的客户端id");
        }
        String key = configuration.getRedisKey() + clientId;
        boolean hasKey = startRedisTools.hasKey(key);
        if (!hasKey) {
            throw new StartBusException("您的密钥已经过期，请重新获取密钥");
        }
        String clientServicePrivateKey = startRedisTools.get(key).toString();
        dataStr = model.getData();
        try {
            dataStr = Sm2Util.decrypt(dataStr, clientServicePrivateKey);
        } catch (Exception e) {
            throw new StartBusException("服务端解密数据失败请检查您的加密或解密算法");
        }
        inputStream = new ByteArrayInputStream(dataStr.getBytes(StandardCharsets.UTF_8));
        return new StartHttpInputMessage(inputMessage.getHeaders(), inputStream);
    }


}
